package com.da.core.stack;

import com.da.core.JSONParseException;
import com.da.core.JsonArray;
import com.da.core.JsonObject;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author da
 * @time 2025/3/5 上午 10:05
 * json解析器,使用正则表达式解析和栈去解析json
 */
public class JsonParser {
    // 用来匹配json中的token的正则
    @SuppressWarnings("all")
    private static final Pattern TOKEN_PATTERN = Pattern.compile(
            "(\\{|}|\\[|]|:|,)|true|false|null|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|\"(?:\\\\[\\\\\"bfnrt/]|[^\\\\\"])*\""
    );

    /**
     * json节点
     */
    static class Context {
        Object value; // 当前值
        String key; // 当前键
        Context parent; // 父节点

        /**
         * 构造函数
         *
         * @param value  当前值
         * @param parent 父节点
         */
        Context(Object value, Context parent) {
            this.value = value;
            this.parent = parent;
        }
    }


    /**
     * 解析json
     *
     * @param json json字符串
     * @return 解析后的对象(JsonObject或JsonArray)
     */
    public Object parse(String json) {
        // 使用正则表达式匹配json中的token
        Matcher matcher = TOKEN_PATTERN.matcher(json);
        // 将匹配到的token放入tokens列表中
        List<String> tokens = new ArrayList<>();
        while (matcher.find()) {
            tokens.add(matcher.group());
        }
        // 使用栈来解析tokens
        Stack<Context> stack = new Stack<>();
        //初始化一个根节点
        Context current = new Context(null, null);
        //遍历tokens
        for (String token : tokens) {
            switch (token) {
                // 解析对象
                case "{":
                    // 如果是 { 就需要创建一个新的JsonObject
                    JsonObject obj = new JsonObject();
                    // 如果当前节点的key不为空,说明当前节点是一个键值对
                    if (null != current.key) {
                        // 将当前节点放入栈中
                        stack.push(current);
                        // 并且把当前节点更新为新建的JsonObject,并且指定父级节点为刚刚压入栈的节点的父节点
                        current = new Context(obj, current.parent);
                    } else {
                        // 如果当前节点的key为空,说明当前节点是一个对象,需要把当前的节点的值指定为新建的JsonObject
                        current.value = obj;
                    }
                    break;
                // 解析数组
                case "[":
                    // 如果是 [ 就需要创建一个新的JsonArray
                    JsonArray array = new JsonArray();
                    // 如果当前节点的key不为空,说明当前节点是一个键值对
                    if (null != current.key) {
                        // 将当前节点放入栈中
                        stack.push(current);
                        // 并且把当前节点更新为新建的JsonArray,并且指定父级节点为刚刚压入栈的节点的父节点
                        current = new Context(array, current.parent);
                    } else {
                        // 如果当前节点的key为空,说明当前节点是一个数组,需要把当前的节点的值指定为新建的JsonArray
                        current.value = array;
                    }
                    break;
                case "}":
                case "]":
                    // 如果是 } 或者 ] 说明对象或者数组结束了,需要把刚刚栈里面保存的节点弹出处理
                    if (!stack.isEmpty()) {
                        Context parent = stack.pop();
                        //如果弹出节点的值是JsonArray
                        if (parent.value instanceof JsonArray) {
                            //把当前的值添加到数组里面
                            ((JsonArray) parent.value).add(current.value);
                        } else if (parent.value instanceof JsonObject) {
                            //如果弹出节点的值是JsonObject,那么需要把当前的值添加到对象里面
                            ((JsonObject) parent.value).put(parent.key, current.value);
                            //设置key为空,因为已经处理过了
                            parent.key = null;
                        }
                        //更新当前节点为弹出的节点
                        current = parent;
                    }
                    break;
                case ":":
                case ",":
                    // 如果是 : 或者 , 就跳过不解析
                    break;
                default:
                    // 解析键或者值
                    Object value = parseValue(token);
                    // 把值更新到当前节点上
                    processValue(current, value);
                    break;
            }
        }
        // 返回节点的值
        return current.value;
    }

    /**
     * 解析值
     *
     * @param token 字符串、数字、布尔值等
     * @return 解析后对应的java类型的值
     */
    private Object parseValue(String token) {
        if ("true".equals(token)) {
            return true;
        }
        if ("false".equals(token)) {
            return false;
        }
        if ("null".equals(token)) {
            return null;
        }
        //字符串
        if (token.startsWith("\"") && token.endsWith("\"")) {
            return token.substring(1, token.length() - 1);
        }
        //Long类型
        if (token.matches("-?\\d+")) {
            return Long.parseLong(token);
        }
        //Double类型
        if (token.matches("-?\\d+\\.\\d+([eE][+-]?\\d+)?")) {
            return Double.parseDouble(token);
        }
        throw new JSONParseException("无效值: " + token);
    }

    /**
     * 把值给到当前的节点
     *
     * @param ctx   当前的节点
     * @param value 要处理的值
     */
    private void processValue(Context ctx, Object value) {
        //如果是数组
        if (ctx.value instanceof JsonArray) {
            //直接添加值到数组中
            ((JsonArray) ctx.value).add(value);
        } else if (ctx.value instanceof JsonObject) {
            //如果是对象
            if (null == ctx.key) {
                //如果键为空,说明当前要处理的值是键名
                ctx.key = (String) value;
            } else {
                //如果键不为空,说明当前要处理的值是键值
                ((JsonObject) ctx.value).put(ctx.key, value);
                //设置key为空,因为已经处理过了
                ctx.key = null;
            }
        } else {
            //不是上面的2种情况,说明当前是在添加JsonObject或者JsonArray
            ctx.value = value;
        }
    }
}
